package cn.lcy.knowledge.analysis.ontology.query.service;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.jena.rdf.model.Statement;

import com.hankcs.hanlp.dictionary.CoreSynonymDictionary;

import cn.lcy.knowledge.analysis.enums.NounsTagEnum;
import cn.lcy.knowledge.analysis.ontology.query.dao.QueryDAOImpl;
import cn.lcy.knowledge.analysis.ontology.query.dao.QueryDAOI;
import cn.lcy.knowledge.analysis.sem.model.AnswerStatement;
import cn.lcy.knowledge.analysis.sem.model.PolysemantNamedEntity;
import cn.lcy.knowledge.analysis.sem.model.PolysemantStatement;
import cn.lcy.knowledge.analysis.sem.model.QueryResult;
import cn.lcy.knowledge.analysis.sem.model.SemanticGraph;
import cn.lcy.knowledge.analysis.sem.model.SemanticGraphEdge;
import cn.lcy.knowledge.analysis.sem.model.SemanticGraphVertex;
import cn.lcy.knowledge.analysis.sem.model.Word;

public class QueryServiceImpl implements QueryServiceI {

    private volatile static QueryServiceI singleInstance;

    private static QueryDAOI queryDAO;

    static {
        queryDAO = QueryDAOImpl.getInstance();
    }

    /**
     * 私有化构造方法，实现单例模式
     */
    private QueryServiceImpl() {
    }

    /**
     * 获取单例
     *
     * @return
     */
    public static QueryServiceI getInstance() {
        if (singleInstance == null) {
            synchronized (QueryServiceImpl.class) {
                if (singleInstance == null) {
                    singleInstance = new QueryServiceImpl();
                }
            }
        }
        return singleInstance;
    }


    /**
     * 根据语义图构造陈述
     */
    @Override
    public List<AnswerStatement> createStatement(SemanticGraph semanticGraph) {
        // 陈述集合
        List<AnswerStatement> answerStatements = new ArrayList<AnswerStatement>();
        // 解析语义图  构造陈述
        for (SemanticGraphVertex sourceVertex : semanticGraph.getAllVertices()) {
            for (SemanticGraphEdge edge : semanticGraph.getOutgoingEdges(sourceVertex)) {
                SemanticGraphVertex destVertex = new SemanticGraphVertex();
                for (SemanticGraphVertex v : semanticGraph.getAllVertices()) {
                    for (SemanticGraphEdge e : semanticGraph.getIncomingEdges(v)) {
                        if (e.equals(edge)) {
                            destVertex = v;
                        }
                    }
                }
                AnswerStatement answerStatement = new AnswerStatement();
	    		/* for(PolysemantEntity polysemantEntity : polysemantEntities) {
	    			 if(sourceVertex.getCoNLLWord().ID == polysemantEntity.getPosition()) {
	    				 sourceVertex.getCoNLLWord().LEMMA = polysemantEntity.getUUID();
	    			 }
	    			 if(destVertex.getCoNLLWord().ID == polysemantEntity.getPosition()) {
	    				 destVertex.getCoNLLWord().LEMMA = polysemantEntity.getUUID();
	    			 }
	    		 }*/
                answerStatement.setSubject(sourceVertex.getWord());
                answerStatement.setPredicate(edge.getWord());
                answerStatement.setObject(destVertex.getWord());
                answerStatements.add(answerStatement);
            }
        }
        Collections.sort(answerStatements);
        return answerStatements;
    }

    /**
     * 使用笛卡尔积算法获取因为同名实体而造成的所有歧义可能
     * 假设原始断言为 x-y, y-z, z-?answer,
     * 其中 假设 x 有 m 种同名实体, y 有 n 种同名实体, z 有 k 种同名实体,
     * 则共有 m * n * k 种歧义可能.
     */
    @Override
    public List<PolysemantStatement> createPolysemantStatements(List<AnswerStatement> answerStatements) {
        List<PolysemantStatement> polysemantStatements = new ArrayList<PolysemantStatement>();
        // key为实体的位置 value为该位置同名实体的个数
        Map<Integer, Integer> nums = new LinkedHashMap<Integer, Integer>();
        // 第一步：记录每个实体的同名实体的个数
        int index = 0;
        for (AnswerStatement answerStatement : answerStatements) {
            if (index == 0) {
                Word subject = answerStatement.getSubject();
                Word object = answerStatement.getObject();
                int subjectPolysemantNamedEntitiesSize = subject.getPolysemantNamedEntities().size();
                int objectPolysemantNamedEntitiesSize = object.getPolysemantNamedEntities().size();
                if (subjectPolysemantNamedEntitiesSize != 0) {
                    nums.put(subject.getPosition(), subjectPolysemantNamedEntitiesSize);
                }
                if (objectPolysemantNamedEntitiesSize != 0) {
                    nums.put(object.getPosition(), objectPolysemantNamedEntitiesSize);
                }
            } else {
                Word object = answerStatement.getObject();
                int objectPolysemantNamedEntitiesSize = object.getPolysemantNamedEntities().size();
                if (objectPolysemantNamedEntitiesSize != 0) {
                    nums.put(object.getPosition(), objectPolysemantNamedEntitiesSize);
                }
            }
            index++;
        }

        int counter[] = new int[nums.size()];
        int loopLength = 1;
        for (Entry<Integer, Integer> num : nums.entrySet()) {
            loopLength *= num.getValue();
        }


        int counterIndex = nums.size() - 1;
        for (int i = 0; i < loopLength; i++) {
            PolysemantStatement polysemantStatement = new PolysemantStatement();
            List<AnswerStatement> newAnswerStatements = new ArrayList<AnswerStatement>();
            for (AnswerStatement answerStatement : answerStatements) {
                Word subject = answerStatement.getSubject();
                Word predicate = answerStatement.getPredicate();
                Word object = answerStatement.getObject();
                Word subjectNew = new Word();
                Word predicateNew = new Word();
                Word objectNew = new Word();
                try {
                    BeanUtils.copyProperties(subjectNew, subject);
                    BeanUtils.copyProperties(predicateNew, predicate);
                    BeanUtils.copyProperties(objectNew, object);
                } catch (IllegalAccessException | InvocationTargetException e1) {
                    e1.printStackTrace();
                }

                List<PolysemantNamedEntity> subjectPolysemantNamedEntities = new ArrayList<PolysemantNamedEntity>();
                for (PolysemantNamedEntity polysemantNamedEntity : subject.getPolysemantNamedEntities()) {
                    PolysemantNamedEntity polysemantNamedEntityNew = new PolysemantNamedEntity();
                    try {
                        BeanUtils.copyProperties(polysemantNamedEntityNew, polysemantNamedEntity);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        e.printStackTrace();
                    }
                    subjectPolysemantNamedEntities.add(polysemantNamedEntityNew);
                }
                subjectNew.setPolysemantNamedEntities(subjectPolysemantNamedEntities);

				/*List<PolysemantNamedEntity> predicatePolysemantNamedEntities = new ArrayList<PolysemantNamedEntity>();
				for(PolysemantNamedEntity polysemantNamedEntity : predicate.getPolysemantNamedEntities()) {
					PolysemantNamedEntity polysemantNamedEntityNew  = new PolysemantNamedEntity();
					BeanUtils.copyProperties(polysemantNamedEntity, polysemantNamedEntityNew);
					predicatePolysemantNamedEntities.add(polysemantNamedEntityNew);
				}
				predicateNew.setPolysemantNamedEntities(predicatePolysemantNamedEntities);*/

                List<PolysemantNamedEntity> objectPolysemantNamedEntities = new ArrayList<PolysemantNamedEntity>();
                for (PolysemantNamedEntity polysemantNamedEntity : object.getPolysemantNamedEntities()) {
                    PolysemantNamedEntity polysemantNamedEntityNew = new PolysemantNamedEntity();
                    try {
                        BeanUtils.copyProperties(polysemantNamedEntityNew, polysemantNamedEntity);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        e.printStackTrace();
                    }
                    objectPolysemantNamedEntities.add(polysemantNamedEntityNew);
                }
                objectNew.setPolysemantNamedEntities(objectPolysemantNamedEntities);

                AnswerStatement answerStatementNew = new AnswerStatement();
                answerStatementNew.setSubject(subjectNew);
                answerStatementNew.setPredicate(predicateNew);
                answerStatementNew.setObject(objectNew);
				/*int subjectPolysemantNamedEntitiesSize = subject.getPolysemantNamedEntities().size();
				int objectPolysemantNamedEntitiesSize = object.getPolysemantNamedEntities().size();*/


                int subscript = 0;
                for (Entry<Integer, Integer> num : nums.entrySet()) {
                    if (num.getKey() == subjectNew.getPosition()) {
                        subjectNew.active(subjectNew.getPolysemantNamedEntities().get(counter[subscript]));
                    }
                    if (num.getKey() == objectNew.getPosition()) {
                        objectNew.active(objectNew.getPolysemantNamedEntities().get(counter[subscript]));
                    }
                    ++subscript;
                }
                newAnswerStatements.add(answerStatementNew);
            }
            this.handle(nums, counter, counterIndex);
            polysemantStatement.setAnswerStatements(newAnswerStatements);
            polysemantStatements.add(polysemantStatement);
        }
        return polysemantStatements;
    }

    public void handle(Map<Integer, Integer> nums, int counter[], int counterIndex) {
        if (counterIndex < 0) {
            return;
        }
        counter[counterIndex]++;
        int index = 0;
        int length = 0;
        for (Entry<Integer, Integer> num : nums.entrySet()) {
            if (index == counterIndex) {
                length = num.getValue();
            }
            ++index;
        }
        if (counter[counterIndex] >= length) {
            counter[counterIndex] = 0;
            counterIndex--;
            if (counterIndex >= 0) {
                this.handle(nums, counter, counterIndex);
            }
            counterIndex = nums.size() - 1;
        }
    }

    /**
     * 实体消岐
     */
    public List<AnswerStatement> individualsDisambiguation(List<AnswerStatement> answerStatements) {
        List<AnswerStatement> individualsDisambiguationStatements = new ArrayList<AnswerStatement>();

        for (AnswerStatement individualsDisambiguationStatement : answerStatements) {


            if (individualsDisambiguationStatement.getSubject().acquireActiveEntity() != null) {
                // 如果主语是问号的话 就不需要进行实体消岐了
                if (individualsDisambiguationStatement.getSubject().getPosition() < 1024) {
                    // 查询本体库中是否有该实体
                    // TODO 改为注入 这里是为测试方便
                    // boolean individualExist = new JenaDAOImpl().individualExist(myStatement.getSubject().LEMMA);
                    // 设置实体消岐前的实体名 方便前端读取
                    System.out.println("进入的实体：" + individualsDisambiguationStatement.getSubject().getName());
                    // TODO 改为注入 这里是为测试方便 individualDisambiguation为UUID

                    String individualDisambiguationName = null;
                    String sameEntityUUID = null;
                    if (individualsDisambiguationStatement.getSubject().acquireActiveEntity() != null) {
                        if (individualsDisambiguationStatement.getSubject().acquireActiveEntity().getIsAliases().equals("0")) { // 如果该实体为实体别名
                            sameEntityUUID = queryDAO.querySameIndividual(individualsDisambiguationStatement.getSubject().acquireActiveEntity().getUUID());
                        }

                    }
                    String entityUUID = sameEntityUUID == null ? individualsDisambiguationStatement.getSubject().acquireActiveEntity().getUUID() : sameEntityUUID;

                    individualDisambiguationName = queryDAO.queryIndividualComment(entityUUID);  // TODO 修改为注入 本体库中的comment表示该实体的实体名
                    individualsDisambiguationStatement.getSubject().setDisambiguationName(individualDisambiguationName);
                    individualsDisambiguationStatement.getSubject().setDisambiguationUUID(entityUUID);
                    System.out.println("消岐后的实体:" + individualsDisambiguationStatement.getSubject().getDisambiguationName());
                    // 设置实体消岐后的实体名 方便前端读取
                    //myStatement.setSubjectDisambiguation(myStatement.getSubject().LEMMA);
                }
            }
            individualsDisambiguationStatements.add(individualsDisambiguationStatement);
        }
        return individualsDisambiguationStatements;
    }

    /**
     * 谓语消岐
     */
    public List<AnswerStatement> predicateDisambiguation(List<AnswerStatement> answerStatements) {
        List<AnswerStatement> predicateDisambiguationStatements = new ArrayList<AnswerStatement>();
        int index = 0;
        for (AnswerStatement predicateDisambiguationStatement : answerStatements) {
            Long minDistance = 9223372036854775807L;
            // 问句中的谓语
            String predicateOld = predicateDisambiguationStatement.getPredicate().getName();
            String mostSimilarPredicate = predicateOld;

            //String object = answerStatement.getObject().getName();
            // 设置消岐前的谓语 方便前端读取
            //answerStatement.setPredicateName(predicateOld);
            // TODO 改为注入 这里是为测试方便
            // 如果主语不是问号 则进行获取主语的相关断言
            List<Statement> statements = null;
            if (predicateDisambiguationStatement.getSubject().getPosition() < 1024) {
                String subjectUUID = predicateDisambiguationStatement.getSubject().getDisambiguationUUID();
                statements = queryDAO.getStatementsBySubject(subjectUUID);
            } else {
                // 如果主语是问号 则需要进行一次前断言的查询 然后消岐
                List<AnswerStatement> preStatements = new ArrayList<AnswerStatement>();
                AnswerStatement preStatement = answerStatements.get(index - 1);
                AnswerStatement preStatementNew = new AnswerStatement();
                try {
                    BeanUtils.copyProperties(preStatementNew, preStatement);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                Word subject = preStatement.getSubject();
                Word predicate = preStatement.getPredicate();
                Word object = preStatement.getObject();
                Word subjectNew = new Word();
                Word predicateNew = new Word();
                Word objectNew = new Word();
                try {
                    BeanUtils.copyProperties(subjectNew, subject);
                    BeanUtils.copyProperties(predicateNew, predicate);
                    BeanUtils.copyProperties(objectNew, object);
                } catch (IllegalAccessException | InvocationTargetException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                preStatementNew.setSubject(subjectNew);
                preStatementNew.setPredicate(predicateNew);
                preStatementNew.setObject(objectNew);
                preStatements.add(preStatementNew);

                List<AnswerStatement> preQueryStatements = new QueryServiceImpl().createQueryStatements(preStatements);
                String SPARQL = this.createSparql(preQueryStatements);
                QueryResult result = queryDAO.queryOntology(SPARQL);
                Word subjectUpdate = predicateDisambiguationStatement.getSubject();
                // 更新主语
                subjectUpdate.setName(result.getAnswers().get(0).getContent().split(":")[1]);
                predicateDisambiguationStatement.setSubject(subjectUpdate);
                String subjectUpdateStr = predicateDisambiguationStatement.getSubject().getName();
                statements = queryDAO.getStatementsBySubject(subjectUpdateStr);


                /*statements = new JenaDAOImpl().getStatementsByObject(object);*/
            }

            // 迭代所有以Subject为主语的短语 寻找最相似的谓语
            for (Statement statement : statements) {
                // 本体库中的谓语
                String predicate = statement.getPredicate().getURI().split("#")[1];
                // 计算句子中谓语和本体库中谓语的语义相似度
                String[] predicateArray = predicate.split("有");
                if (predicateArray.length >= 2) {
                    Long distance = CoreSynonymDictionary.distance(predicateOld, predicateArray[1]);
                    if (distance < minDistance) {
                        minDistance = distance;
                        mostSimilarPredicate = predicateArray[1];
                    }
                    System.out.println(predicateOld + " 和  " + predicateArray[1] + ":" + distance);
                } else {
                    Long distance = CoreSynonymDictionary.distance(predicateOld, predicateArray[0]);
                    if (distance < minDistance) {
                        minDistance = distance;
                        mostSimilarPredicate = predicateArray[0];
                    }
                    System.out.println(predicateOld + " 和  " + predicateArray[0] + ":" + distance);
                }
                System.out.println();
            }
            predicateDisambiguationStatement.getPredicate().setDisambiguationName(mostSimilarPredicate);
            // 设置谓语消岐的结果 方便前端读取
            //answerStatement.setPredicateDisambiguation(mostSimilarPredicate);
            predicateDisambiguationStatements.add(predicateDisambiguationStatement);
            System.out.println("谓语消岐距离：" + minDistance);
            System.out.println("谓语消岐为：" + mostSimilarPredicate);
            ++index;
        }
        return predicateDisambiguationStatements;
    }

    /**
     * 构造查询断言
     */
    @Override
    public List<AnswerStatement> createQueryStatements(List<AnswerStatement> answerStatements) {
        List<AnswerStatement> queryStatements = new ArrayList<AnswerStatement>();
        int i = 0;
        for (AnswerStatement answerStatement : answerStatements) {
            answerStatement.getSubject().setDisambiguationName("mymo:" + answerStatement.getSubject().getDisambiguationUUID());

            if (NounsTagEnum.isIncludeEnumElement(answerStatement.getPredicate().getCpostag())) {
                answerStatement.getPredicate().setDisambiguationName("mymo:" + "有" + answerStatement.getPredicate().getDisambiguationName());
            } else {
                answerStatement.getPredicate().setDisambiguationName("mymo:" + answerStatement.getPredicate().getDisambiguationName());
            }

            int ch = 'a' + i;
            String preMask = "?" + (char) (ch - 1);
            String mask = "?" + (char) ch;
            answerStatement.getObject().setDisambiguationName(mask);
            if (i > 0) {
                answerStatement.getSubject().setDisambiguationName(preMask);
            }
            queryStatements.add(answerStatement);
            ++i;
        }
        return queryStatements;
    }

    // TODO 根据断言集合构建查询语句
    @Override
    public String createSparql(List<AnswerStatement> answerStatements) {
        // 断言集合
        int size = answerStatements.size();
        // 取出最后一个断言
        AnswerStatement lastStatement = answerStatements.get(size - 1);
        String queryMask = lastStatement.getObject().getDisambiguationName();
        String SPARQL = "SELECT " + queryMask + "  WHERE {\n";
        for (AnswerStatement answerStatement : answerStatements) {
            // 如果谓语是名词
            if (NounsTagEnum.isIncludeEnumElement(answerStatement.getPredicate().getCpostag())) {
                SPARQL += answerStatement.getSubject().getDisambiguationName() + "  " + answerStatement.getPredicate().getDisambiguationName() + "  " + answerStatement.getObject().getDisambiguationName() + ".\n";
            } else {
                SPARQL += answerStatement.getSubject().getDisambiguationName() + "  " + answerStatement.getPredicate().getDisambiguationName() + "  " + answerStatement.getObject().getDisambiguationName() + ".\n";
            }
        }
        SPARQL += "}";
        return SPARQL;
    }

    /**
     * 根据断言集合多次构建查询语句   多次构造 多次调用 每次调用的时候传入一个 二个 ...断言
     *
     * @param myStatements
     * @return
     */
    public List<String> createSparqls(List<AnswerStatement> answerStatements) {
        List<String> SPARQLS = new ArrayList<String>();

        int size = answerStatements.size();
        for (int num = 0; num < size; num++) {
            List<AnswerStatement> statements = new ArrayList<AnswerStatement>();
            for (int i = 0; i <= num; i++) {
                statements.add(answerStatements.get(i));
            }
            String SPARQL = this.createSparql(statements);
            SPARQLS.add(SPARQL);
        }
        return SPARQLS;
    }

    @Override
    public QueryResult queryOntology(String sparql) {
        return queryDAO.queryOntology(sparql);
    }

    @Override
    public String queryIndividualComment(String individualName) {
        return queryDAO.queryIndividualComment(individualName);
    }

}
